package com.lidong.lowcode.core.config.syslog;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lidong.config.domain.common.CurrentUser;
import com.lidong.config.domain.logs.SysLogDTO;
import com.lidong.config.domain.rabbitqueue.QueueConst;
import com.lidong.lowcode.core.domain.common.StrConstant;
import com.lidong.toolkit.constant.LogConst;
import com.lidong.toolkit.utils.UuidUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Description：
 * @Author：LiDong
 * @Create：2021/1/25
 * @Version：1.0.0
 */
// @Aspect
// @Component
public class SystemLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 换行符
     */
    private static final String LINE_SEPARATOR = System.lineSeparator();

    /**
     * 使用 threadlocal:around 相关的参数
     */
    private static final ThreadLocal<Map<String, Object>> threadLocalOne = new ThreadLocal<>();

    /**
     * doBefore()中相关的参数
     */
    private static final ThreadLocal<Map<String, Object>> threadLocalTwo = new ThreadLocal<>();

    /**
     * 以自定义 @SysLog 注解为切点
     */
    @Pointcut("@annotation(com.lidong.lowcode.core.config.syslog.SysLog)")
    public void sysLog() {
    }

    /**
     * 在切点之前织入
     */
    @Before("sysLog()")
    public void doBefore(JoinPoint joinPoint) {
        try {
            // 开始打印请求日志
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
            CurrentUser user = (CurrentUser) request.getSession().getAttribute("user");
            // 获取 @sysLog 注解的描述信息
            String methodDescription = this.getAspectLogDescription(joinPoint);
            // 打印请求相关参数
            logger.info("========================================== Start ==========================================");
            // 打印请求 url
            logger.info("URL            : {}", request.getRequestURL().toString());
            // 打印描述信息
            logger.info("Description    : {}", methodDescription);
            // 打印 Http method
            logger.info("HTTP Method    : {}", request.getMethod());
            // 打印调用 controller 的全路径以及执行方法
            logger.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
            // 打印请求的 IP
            logger.info("IP             : {}", request.getRemoteAddr());
            // 打印请求入参
            Object[] args = joinPoint.getArgs();
            Object arg = 0 < args.length ? args[1] : null;
            logger.info("Request Args   : {}", arg);

            SysLogDTO sysLogDTO = new SysLogDTO();
            sysLogDTO.setId(UuidUtils.randomUuid());
            sysLogDTO.setUrl(String.valueOf(request.getRequestURL()));
            sysLogDTO.setMethodname(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "()");
            if (Objects.nonNull(arg) && !StrConstant.STR_NULL.equals(arg)) {
                sysLogDTO.setArgs(JSON.toJSONString(arg));
            }
            sysLogDTO.setMethoddescription(methodDescription);
            sysLogDTO.setRemoteaddr(request.getRemoteAddr());
            sysLogDTO.setRecordertime(new Date());
            sysLogDTO.setUsercode(Objects.isNull(user) ? null : user.getUsercode());
            Map map = JSON.parseObject(JSON.toJSONString(sysLogDTO), Map.class);
            Map<String, Object> mp = new HashMap<>(16);
            threadLocalTwo.set(mp);
            threadLocalTwo.get().putAll(map);
        } catch (Exception e) {
            logger.error("=====> doBefore()出错：{}", e.getMessage());
        }
    }

    private String getAspectLogDescription(JoinPoint joinPoint) {
        return null;
    }

    /**
     * 在切点之后织入
     */
    @After("sysLog()")
    public void doAfter() {
        try {
            // 接口结束后换行，方便分割查看
            logger.info("=========================================== End ==========================================={}", LINE_SEPARATOR);
            Map<String, Object> mapOne = threadLocalOne.get();
            Map<String, Object> mapTwo = threadLocalTwo.get();
            mapTwo.putAll(mapOne);
            // 发送到rabbitmq的队列中
            rabbitTemplate.convertAndSend(QueueConst.GLOBAL_LOG_QUEUE, JSON.toJSONString(mapTwo));
        } catch (Exception e) {
            logger.error("=====> doAfter()出错：{}", e.getMessage());
        } finally {
            threadLocalOne.remove();
            threadLocalTwo.remove();
        }
    }

    /**
     * 环绕
     */
    @Around("sysLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint)  {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            logger.error(LogConst.ERROR,throwable);
        }
        long counttime = System.currentTimeMillis() - startTime;
        // 打印出参
        logger.info("Response Args  : {}", JSON.toJSON(result));
        // 执行耗时
        logger.info("Time-Consuming : {} ms", counttime);
        Map<String, Object> map = new HashMap<>(16);
        threadLocalOne.set(map);
        threadLocalOne.get().put("countTime", counttime);
        return result;
    }
}
